#include "MouseController.h"
#include "core/utils/LimelightUtils.h"
#include "core/utils/XComponentUtils.h"
#include <Limelight.h>
#include <unordered_set>

#undef LOG_DOMAIN
#undef LOG_TAG
#define LOG_DOMAIN  0x0
#define LOG_TAG     "MouseController"
#include <hilog/log.h>

namespace {
constexpr bool DEBUG = false;
}

MouseController::MouseController() {
    loop = std::thread([&] {
        while (!stopFlag) {
            {
                std::mutex m;
                std::unique_lock l(m);
                using namespace std::chrono_literals;
                if (stopCond.wait_for(l, 10ms, [&] { return stopFlag.load(); })) {
                    return;
                }
            }
            if (refWidth <= 0 || refHeight <= 0) {
                continue;
            }
            if (!updated) {
                continue;
            }
            if constexpr (DEBUG) {
                OH_LOG_DEBUG(LOG_APP, "Reporting mouse position (%{public}f,%{public}f) of (%{public}f,%{public}f)",
                    x.load(), y.load(), refWidth.load(), refHeight.load());
            }
            LI_CALL_OR_ELSE(SendMousePositionEvent(x.load(), y.load(), refWidth.load(), refHeight.load()), return);
            updated = false;
        }
    });
}

MouseController::~MouseController() {
    stopFlag = true;
    stopCond.notify_all();
    if (loop.joinable()) {
        loop.join();
    }
}

namespace {
using LiButton = int;
const std::unordered_map<OH_NativeXComponent_MouseEventButton, LiButton> buttonMap = {
    {OH_NATIVEXCOMPONENT_LEFT_BUTTON, BUTTON_LEFT},
    {OH_NATIVEXCOMPONENT_MIDDLE_BUTTON, BUTTON_MIDDLE},
    {OH_NATIVEXCOMPONENT_RIGHT_BUTTON, BUTTON_RIGHT},
    {OH_NATIVEXCOMPONENT_FORWARD_BUTTON, BUTTON_X1},
    {OH_NATIVEXCOMPONENT_BACK_BUTTON, BUTTON_X2},
};
using LiButtonAction = char;
const std::unordered_map<OH_NativeXComponent_MouseEventAction, LiButtonAction> actionMap = {
    {OH_NATIVEXCOMPONENT_MOUSE_PRESS, BUTTON_ACTION_PRESS},
    {OH_NATIVEXCOMPONENT_MOUSE_RELEASE, BUTTON_ACTION_RELEASE},
};
}

void MouseController::handle(OH_NativeXComponent *const xcomp, void *const win, const MouseEvent &event) {
    /* handle button actions */
    if (actionMap.contains(event.action)) {
        for (const auto &[ohBtn, liBtn] : buttonMap) {
            if (event.button & ohBtn) {
                LI_CALL_OR_ELSE(SendMouseButtonEvent(actionMap.at(event.action), liBtn), return);
            }
        }
    }
    /* handle movement */
    struct {
        uint64_t width;
        uint64_t height;
    } renderGeometry;
    XCOMP_CALL_OR_ELSE(GetXComponentSize(xcomp, win, &renderGeometry.width, &renderGeometry.height), return);
    updateCurrentPosition(event.x, event.y, renderGeometry.width, renderGeometry.height);
}

void MouseController::updateCurrentPosition(float x, float y, float refWidth, float refHeight) {
    const auto update = [&] (auto &lhs, const auto &rhs) {
        if (lhs == rhs) {
            return;
        }
        lhs = rhs;
        updated = true;
    };
    update(this->x, x);
    update(this->y, y);
    update(this->refWidth, refWidth);
    update(this->refHeight, refHeight);
}
